import {
  Id,
  Ig,
} from './entry'

import {
  Use,
  Identifier,
  Import,
  Specifier,
  Version,
  Strings
} from '../ast/types'

import sign from '../ast/sign'

const
  COMMA = sign[','],
  S     = sign['\''],
  L     = sign['['],
  R     = sign[']'],
  B     = sign['!'],
  IDENT = sign.ident

function bePath (s) {
  // ALPHA+ [ ['/' / '-' ]? ALPNUM+ ]*
  let start = s.start

  if (!s.begin().isAlpha()) s.unexpected()

  while (s.drop().rune !== 0x27) {
    if (s.isAlpha() || s.isDigit()) continue

    if (s.rune === 0x2F) {
      if (s.drop().isAlpha()) continue
    } else if (s.rune === 0x2D
      && (s.drop().isAlpha() || s.isDigit())
    ) continue

    s.unexpected()
  }

  let path = s.text()

  if (path === 'any')
    s.fail('invalid import path of module', start)

  return new Strings(path)
    .start(start).end(s.drop().end)
}

function version (s) {
  // x(0 - 255).y(0 - 65535).z(0 - 255)
  let x = 0, y = 0, z = 0, v = 0

  while (s.isDigit()) x = x * 10 + s.step() - 0x30

  if (!s.dot() || !s.isDigit()) s.unexpected()
  while (s.isDigit()) y = y * 10 + s.step() - 0x30

  if (!s.dot() || !s.isDigit()) s.unexpected()
  while (s.isDigit()) z = z * 10 + s.step() - 0x30

  v = x * 0x1000000 + y * 0x100 + z

  if (!v || x > 0xFF || y > 0xFFFF || z > 0xFF)
    s.fail('illegal version')
  return v
}

export default function (s) {
  let
    node = new Use().start(s.start),
    sign = s.beNext()

  s.lead()

  while (sign) {
    let
      path = sign === S
        && bePath(s.beAlign())
        || s.fail(sign),

      min = new Version(
        s.sps().begin().isDigit() && version(s)
        || s.minus() && 1
        || 0
      ).start(s.start).end(s.end),

      max = new Version(
        s.minus()
          ? s.isDigit() && version(s.begin()) || 0
          : s.isDigit() && version(s.begin()) || min.value
      ).start(s.start).end(s.end),
      x = new Import(
        path,
        min,
        max,
        s.sps().begin().isIDS() && s.beId()
        || new Identifier(path.value.split('/').pop())
          .start(s.start).end(s.end)
      )

    sign = s.soRight()

    if (sign === L) {
      sign = s.beMore()
      while (true) {
        x.append(
          new Specifier(
            sign === IDENT
            && s.text() || s.fail(sign),
            s.rune === 0x21
            && s.drop() && true || false
          ).start(s.start).end(s.end)
        )

        sign = s.beMore()
        if (sign === COMMA) sign = s.beMore()
        if (sign === R) break
      }
      sign = s.soRight()
    }

    s.beZero(sign)
    node.append(x)

    sign = s.soMore()
    if (!sign) return node.end(x.loc.end)
  }
}
